#include <iostream>
#include "gtest/gtest.h"

#include "machine.h"
#include "machine_factory.h"
#include "predictors/predictor_link.h"
#include "model.h"
#include "predictors/predictor_base.h"
#include "trace.h"
#include "ioconnections.h"

using namespace std;

TEST(Machine, Predict)
{
  Machine *m = Machine_factory::create(string("eater1D"));

  m->update_input(0.0);

  Predictor_base *p1 = m->get_model(0)->get_predictor(); // eat
  Predictor_base *p2 = m->get_model(1)->get_predictor(); // walk
  Predictor_base *p3 = m->get_model(1)->get_predictor(); // start

  ASSERT_EQ( 0.0, p1->get_input_link(0)->get_value() ); // distance to food at which to eat
  ASSERT_EQ( 10.0, p2->get_input_link(1)->get_value() ); // distance to food
  ASSERT_EQ( 10.0, p3->get_input_link(1)->get_value() ); // distance to food

  // test prediction at t=0.0
  m->predict(0.0);
  p1->calculate_error(1.0);
  p2->calculate_error(1.0);
  p3->calculate_error(1.0);
  ASSERT_EQ( 100.0, p1->get_error() );
  ASSERT_EQ( 0.0, p2->get_error() );

  // test prediction at t=2.0
  //cout << "t2" << endl;
  m->predict(2.0);
  p1->calculate_error(2.0);
  p2->calculate_error(2.0);
  ASSERT_EQ( 25.0, p1->get_error() );
  ASSERT_EQ( 1.0, p2->get_error() );

  m->update_input(2.0);

  // test prediction at t=2.0
  p1->predict(2.0);
  p2->predict(2.0);
  p1->calculate_error(2.0);
  p2->calculate_error(2.0);
  //assert_equal( 49.0, p1->get_error() );
  //assert_equal( 0.0, p2->get_error() );

  // test full cycle
  m->predict(3.0);
  m->update_input(3.0);
  m->activate_models(3.0);
  m->activate_model_output_links();

  //assert_equal( false, p1->get_output_link()->active(), "p1" );
  //assert_equal( true, p2->get_output_link()->active() );

  // test full cycle
  m->predict(10.0);
  m->update_input(10.0);
  m->activate_models(10.0);
  m->activate_model_output_links();

  //assert_equal( true, p1->get_output_link()->active(), "p1" );
  //assert_equal( true, p2->get_output_link()->active() );
}

TEST(Machine, Record_trace)
{
  Machine *m = Machine_factory::create(string("eater1D"));

  // add trace
  Trace *trace = new Trace();

  // add distance to food to trace
  vector<IConnection *> input_connections = m->get_input_connections();
  trace->set_dimension(1);
  trace->set_id(0, 0);
  trace->set_source(0, input_connections[0]->get_value_p());

  m->set_trace(trace);

  for (float t = 1.0; t < 13.0; t += 1.0) {
    m->step(t);
  }

  // todo: ASSERT on trace
  trace->save("test_data/machine_test");

  delete trace;
}
